﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Windows.Input;

using log4net;

using WeatherReport.Interfaces;
using WeatherReport.MVVM;

namespace WeatherReport.ViewModels
{
    public class UserSettingsViewModel : ObservableObject
    {
        private const string DEFAULT_CITY_LIST_RETRIEVAL_ERROR_MESSAGE = "";

        private readonly IWeatherService _weatherService;
        private readonly ILog _logger;
        private IWeatherSettings _settings;

        private RelayCommand _settingsOkCommand;
        private RelayCommand _settingsCancelCommand;

        private List<RegionInfo> _regions;
        private RegionInfo _initialRegion;
        private RegionInfo _selectedRegion;

        private ObservableCollection<string> _cities;
        private string _initialCity;
        private string _selectedCity;

        private bool _isCityListAvailable = true;
        private string _cityListRetrievalErrorMessage = DEFAULT_CITY_LIST_RETRIEVAL_ERROR_MESSAGE;

        private object _cityListLock = new object();

        public event EventHandler<EventArgs<RegionInfo, string>> SettingsOkayed;
        public event EventHandler<EventArgs> SettingsCancelled;

        public ICommand SettingsOkCommand
        {
            get { return _settingsOkCommand; }
        }
        public ICommand SettingsCancelCommand
        {
            get { return _settingsCancelCommand; }
        }

        public List<RegionInfo> Regions
        {
            get { return _regions; }
        }

        public RegionInfo SelectedRegion
        {
            get { return _selectedRegion; }
            set
            {
                if (_selectedRegion != value)
                {
                    _selectedRegion = value;
                    RaisePropertyChanged("SelectedRegion");
                    UpdateCityList();
                }
            }
        }

        public ObservableCollection<string> Cities
        {
            get { return _cities; }
        }

        public string SelectedCity
        {
            get { return _selectedCity; }
            set
            {
                if (_selectedCity != value)
                {
                    _selectedCity = value;
                    RaisePropertyChanged("SelectedCity");
                    CommandManager.InvalidateRequerySuggested();
                }
            }
        }

        public bool IsCityListAvailable
        {
            get { return _isCityListAvailable; }
            set
            {
                if (_isCityListAvailable != value)
                {
                    _isCityListAvailable = value;
                    RaisePropertyChanged("IsCityListAvailable");
                    RaisePropertyChanged("IsCityListUnavailable");
                    CommandManager.InvalidateRequerySuggested();
                }
            }
        }

        public bool IsCityListUnavailable
        {
            get { return !IsCityListAvailable; }
        }

        public string CityListRetrievalErrorMessage
        {
            get { return _cityListRetrievalErrorMessage; }
            set
            {
                if (_cityListRetrievalErrorMessage != value)
                {
                    _cityListRetrievalErrorMessage = value;
                    RaisePropertyChanged("CityListRetrievalErrorMessage");
                }
            }
        }

        public UserSettingsViewModel(IWeatherService weatherService, ILog logger, IWeatherSettings settings)
        {
            if (weatherService == null)
                throw new ArgumentNullException("weatherService");
            if (logger == null)
                throw new ArgumentNullException("logger");
            if (settings == null)
                throw new ArgumentNullException("settings");

            _weatherService = weatherService;
            _logger = logger;
            _settings = settings;

            _settingsOkCommand = new RelayCommand(SettingsOkButton_Clicked, 
                () => (IsCityListAvailable && !String.IsNullOrEmpty(SelectedCity)));
            _settingsCancelCommand = new RelayCommand(SettingsCancelButton_Clicked);

            _regions = CreateRegions(settings.Countries);
            _cities = new ObservableCollection<string>();
   
        }

        public void Init()
        {
            if (!String.IsNullOrEmpty(_settings.SelectedCity))
            {
                _initialCity = _settings.SelectedCity;
                _selectedCity = _settings.SelectedCity;
            }

            if (!String.IsNullOrEmpty(_settings.SelectedCountry))
            {
                RegionInfo selected = _regions.FirstOrDefault(r => r.TwoLetterISORegionName.Equals(
                    _settings.SelectedCountry, StringComparison.InvariantCultureIgnoreCase));
                if (selected != null)
                {
                    _initialRegion = selected;
                    _selectedRegion = selected;
                    UpdateCityList(false);
                }
            }
        }

        private void SettingsOkButton_Clicked()
        {
            _settings.SelectedCountry = SelectedRegion.TwoLetterISORegionName;
            _settings.SelectedCity = SelectedCity;
            _settings.Save();

            _initialRegion = SelectedRegion;
            _initialCity = SelectedCity;

            if(SettingsOkayed != null)
                SettingsOkayed(this, new EventArgs<RegionInfo,string>(SelectedRegion, SelectedCity));
        }

        private void SettingsCancelButton_Clicked()
        {
            _selectedRegion = _initialRegion;
            _selectedCity = _initialCity;

            UpdateCityList(false);
            RaisePropertyChanged("SelectedRegion");
            RaisePropertyChanged("SelectedCity");

            if (SettingsCancelled != null)
                SettingsCancelled(this, EventArgs.Empty);
        }

        private List<RegionInfo> CreateRegions(StringCollection countries)
        {
            List<RegionInfo> result = new List<RegionInfo>(countries.Count);
            foreach (string country in countries)
            {
                try
                {
                    RegionInfo r = new RegionInfo(country);
                    result.Add(r);
                }
                catch (ArgumentException) { }
            }
            return result;
        }

        private void UpdateCityList()
        {
            UpdateCityList(true);
        }

        private void UpdateCityList(bool resetSelectedCity)
        {
            using (BackgroundWorker bgworker = new BackgroundWorker() { WorkerReportsProgress = false })
            {
                bgworker.DoWork += (s, e) =>
                {
                    if (SelectedRegion != null)
                    {
                        try
                        {
                            e.Result = _weatherService.GetCitiesByCountry(SelectedRegion.EnglishName);
                        }
                        catch (Services.WeatherServiceException wex)
                        {
                            e.Result = wex;
                        }
                    }
                };

                bgworker.RunWorkerCompleted += (s, e) =>
                {
                    if (e.Result is Services.WeatherServiceException)
                    {
                        Services.WeatherServiceException wex = (Services.WeatherServiceException)e.Result;
                        IsCityListAvailable = false;
                        CityListRetrievalErrorMessage = GetWeatherServiceExceptionDisplayedMessage(wex);
                        _logger.Error("UpdateCityList: Failed to retrieve the city list", wex);
                    }
                    else if (e.Result is IList<string>)
                    {
                        IList<string> cities = (IList<string>)e.Result;
                        string oldSelectedCity = SelectedCity;
                        lock (_cityListLock)
                        {
                            _cities.Clear();
                            foreach (string city in cities.OrderBy(c => c))
                                _cities.Add(city);
                        }
                        if (resetSelectedCity)
                            SelectedCity = null;
                        else
                        {
                            if (_cities.Contains(oldSelectedCity))
                                SelectedCity = oldSelectedCity;
                        }
                        IsCityListAvailable = true;
                        CityListRetrievalErrorMessage = DEFAULT_CITY_LIST_RETRIEVAL_ERROR_MESSAGE;
                    }
                    //_countryCitySetupCompleteEvent.Set();
                };

                bgworker.RunWorkerAsync();
            }
        }

        private string GetWeatherServiceExceptionDisplayedMessage(Services.WeatherServiceException wex)
        {
            return DisplayedErrors.ResourceManager.GetString(wex.Reason.ToString());
        }
    }
}
